Skip to content

feat: single executable - #34

Merged
TitusKirch merged 13 commits into
kirchDev:devfrom
simonostendorf:feat/single-executable
Oct 15, 2026
Merged

TitusKirch merged 13 commits into
kirchDev:devfrom
simonostendorf:feat/single-executable

Conversation

@simonostendorf

Copy link
Copy Markdown
Contributor

Summary

Add static binary using Node SEA.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Internal / chore

Checklist

  • The project's checks pass locally (lint / format / tests, as applicable)
  • Tests added or updated — or not applicable
  • Docs updated (README / inline help / comments) — or not applicable
  • Commits follow Conventional Commits

Related issues

Closes #33

A SEA needs two things the npm bundle cannot give it: a CommonJS entry,
because node's embedder runs the main script as CJS, and the runtime
dependencies inlined, because there is no node_modules beside it. Hence
a second vite config writing `dist/sea/entry.cjs` rather than a format
added to the existing one.

`scripts/sea-build.ts` writes the blob, injects it with postject, and
re-signs on darwin — injecting bytes invalidates the signature the node
binary shipped with, and macOS then refuses to run it. It runs
`--version` out of the finished binary before packaging, which is what
catches a bad injection: both failure modes segfault rather than error.

The embedded runtime is the node that ran the script, so the artifact
carries the version CI resolved from `.nvmrc`.
Walking up to a package.json cannot work inside a SEA — the runtime, the
bundle and the templates are all in the one file being run — so a
downloaded binary reported `unknown / no package.json found above the
binary`, which reads like a defect rather than the most ordinary install
there is. `isSea()` answers it directly.

The branch is covered by swapping `node:sea` in the test: `isSea()` is
false under the runner by construction. The real artifact is checked by
the build script, which runs `--version` out of the binary.
One job per platform, because there is nothing to cross-compile from: a
single executable is the target platform's own node binary with a blob
in it. Each job uploads its archive and a `.sha256` straight to the
release release-please just cut, so no artifact handoff is needed.

Stable releases only. A prerelease publishes to npm without cutting a
GitHub release, so there would be nothing to attach to.
Covers the install path for a machine with no Node on it, including the
checksum step, and says what `info` reports so a downloaded binary is
not mistaken for an installed package.
@TitusKirch
TitusKirch changed the base branch from main to dev October 15, 2026 20:34
A pathname keeps %20 in a path with spaces.
A chunk split off the entry would be missing inside the binary, so the
build refuses one. list now runs out of the binary next to --version, and
a stable archive name lets releases/latest/download resolve.
One workflow for both callers: pull requests touching the build run it
on all four platforms and install the archive the way the README does,
and the release calls it with the tag to attach the archives.
Comment thread .github/workflows/sea.yml Fixed
CodeQL flags a movable tag on a third-party action. GitHub-owned actions
stay on major tags.
@TitusKirch

Copy link
Copy Markdown
Member

Thanks! Retargeted to dev and added on top: lockfile, archives without a version (releases/latest/download works), a one-file guard, sea.yml building all four platforms on PRs and for the release, ADR-0010, and install/update docs.

@TitusKirch
TitusKirch merged commit 19f0104 into kirchDev:dev Oct 15, 2026
14 checks passed
@TitusKirch TitusKirch mentioned this pull request Oct 15, 2026
1 task done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Add static binary

3 participants